﻿using System;
using System.Runtime.InteropServices;

namespace SkeinLibManaged
{   ///<summary>
    ///Core block cipher for Skein hash function.
    ///Can be used for encryption in most (if not all) of the usual modes.
    ///Utility class.  No constructors.
    ///</summary>
    public static partial class ThreeFish
    {   //===HEADER===
        
        //CONSTANTS
        private const UInt64 THREEFISH_PARITY = 0x5555555555555555;  // This is the C_5 constant.

        // SEPTEMBER 2009, THESE VALUES ARE FOR VERSION 1.2
        // This is taken straight from Reference Implementation, but is
	    // named slightly different for clarity, and broken into groups.
	    // R0A means Round 0, First pair of 64 bit integers 
	    // R5F means Round 5, Sixth pair of 64 bit integers

        //ROTATION CONSTANTS USED DURING "MIX" FUNCTIONS
        /// <summary>
        /// Rotation Constants for ThreeFish256
        /// </summary>
        private enum RC256 : byte  
        {   /* Skein256 round rotation constants, 4 64-bit integers, grouped in 2 pairs */
            R0A = 14, R0B = 16,
            R1A = 52, R1B = 57,
            R2A = 23, R2B = 40,
            R3A =  5, R3B = 37,
            R4A = 25, R4B = 33,
            R5A = 46, R5B = 12,
            R6A = 58, R6B = 22,
            R7A = 32, R7B = 32
        };

        /// <summary>
        /// Rotation Constants for ThreeFish512
        /// </summary>
        private enum RC512 : byte
        {   /* Skein512 round rotation constants, 8 64-bit integers, grouped in 4 pairs */
            R0A = 46, R0B = 36, R0C = 19, R0D = 37,
            R1A = 33, R1B = 27, R1C = 14, R1D = 42,
            R2A = 17, R2B = 49, R2C = 36, R2D = 39,
            R3A = 44, R3B =  9, R3C = 54, R3D = 56,
            R4A = 39, R4B = 30, R4C = 34, R4D = 24,
            R5A = 13, R5B = 50, R5C = 10, R5D = 17,
            R6A = 25, R6B = 29, R6C = 39, R6D = 43,
            R7A =  8, R7B = 35, R7C = 56, R7D = 22
        };

        /// <summary>
        /// Rotation Constants for ThreeFish1024
        /// </summary>
        private enum RC1K : byte
        {	/* Skein1024 round rotation constants, 16 64-bit integers, grouped in 8 pairs */
            R0A = 24, R0B = 13, R0C =  8, R0D = 47, R0E =  8, R0F = 17, R0G = 22, R0H = 37,
            R1A = 38, R1B = 19, R1C = 10, R1D = 55, R1E = 49, R1F = 18, R1G = 23, R1H = 52,
            R2A = 33, R2B =  4, R2C = 51, R2D = 13, R2E = 34, R2F = 41, R2G = 59, R2H = 17,
            R3A =  5, R3B = 20, R3C = 48, R3D = 41, R3E = 47, R3F = 28, R3G = 16, R3H = 25,
            R4A = 41, R4B =  9, R4C = 37, R4D = 31, R4E = 12, R4F = 47, R4G = 44, R4H = 30,
            R5A = 16, R5B = 34, R5C = 56, R5D = 51, R5E =  4, R5F = 53, R5G = 42, R5H = 41,
            R6A = 31, R6B = 44, R6C = 47, R6D = 46, R6E = 19, R6F = 42, R6G = 44, R6H = 25,
            R7A =  9, R7B = 48, R7C = 35, R7D = 52, R7E = 23, R7F = 31, R7G = 37, R7H = 20
        };

        //COMMON FUNCTIONS

        /// <summary>
        /// Rotate Left (64-bit word)
        /// </summary>
        /// <param name="inWd">Word to be rotated (by reference).</param>
        /// <param name="inRot">Number of bits to rotate.</param>
        private static void ROTL64(ref UInt64 inWd, Byte inRot)
        {
            inWd = (UInt64)(inWd << inRot) + (UInt64)(inWd >> (64 - inRot));
        }

        /// <summary>
        /// Rotate Right (64-bit word)
        /// </summary>
        /// <param name="inWd">Word to be rotated (by reference).</param>
        /// <param name="inRot">Number of bits to rotate.</param>
        private static void ROTR64(ref UInt64 inWd, Byte inRot)
        {
            inWd = (UInt64)(inWd >> inRot) + (UInt64)(inWd << (64 - inRot));
        }

        /// <summary>
        /// Provides the result of the Sigma K function in the white paper.
        /// Produces a cumulative XOR of all the words of the key array, and XOR's that result with the C_5 constant.
        /// </summary>
        /// <param name="k">The key array.</param>
        /// <returns>The result of the Sigma K function.</returns>
        private static UInt64 UNIQUE_K(params UInt64[] k)
        {   UInt64 result = THREEFISH_PARITY;
            if (k != null)
            {   foreach (UInt64 thing in k)
                {   result ^= thing;
                }
            }
            return result;
        }
    }
}
